    /*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package hps.model;

import java.awt.Graphics;
import java.awt.Image;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import javax.swing.ImageIcon;
import javax.swing.event.EventListenerList;

/**
 *
 * @author $Author: NIKE3500 $
 * @version $Revision: 92 $
 * @date $Date: 2013-01-18 23:26:03 +0000 (Fri, 18 Jan 2013) $
 */
public class Pedestrian implements IInfluence
{

    //static
    private static Image pedestrianImage;
    private static EventListenerList toNotify;
    //Member
    private Point position;
    private Point lastPosition = null;
    private Point destination = null;
    private double velocity = 0;
    private int health = 100;
    private ArrayList<IInfluence> me = new ArrayList<IInfluence>(1);
    private boolean recentlyDied = false;

    public Pedestrian()
    {
        position = new Point();
        me.add(this);
    }

    public Pedestrian(int x, int y)
    {
        this();
        position = new Point(x, y);
    }

    public Pedestrian(Point p)
    {
        this();
        position = p;
    }

    public Point getPos()
    {
        return this.position;
    }

    @Override
    public boolean isAt(Point pos)
    {
        return pos.equals(this.position);
    }

    public double getVelocity()
    {
        return velocity;
    }

    public int getHealth()
    {
        return health;
    }

    public void setHealth(int health)
    {
        this.health = health;
    }

    @Override
    public void draw(Graphics g, Point roomPosition, int singleFieldSize)
    {
        if (pedestrianImage == null)
        {
            pedestrianImage = new ImageIcon(getClass().getResource("/hps/view/HPS_icons/mensch.png")).getImage();
        }
        g.drawImage(pedestrianImage,
                roomPosition.getX() + this.position.getX() * singleFieldSize + 1,
                roomPosition.getY() + this.position.getY() * singleFieldSize + 1,
                singleFieldSize, singleFieldSize, null);
    }

    @Override
    public void setInfluence(FloorField field)
    {
//        byte[][] influence = field.getField();
//        if (influence[position.getX()][position.getY()] < (Byte.MAX_VALUE - 3))
//        {
//            influence[position.getX()][position.getY()] += 3;
//        }
//
//        field.setField(influence);
    }

    /**
     * Fussgaenger verursachen momentan keinen Schaden.
     *
     * @param pedestrians
     */
    @Override
    public void damage(ArrayList<Pedestrian> pedestrians)
    {
        //Verursacht keinen Schaden
    }

    @Override
    public IInfluence clone()
    {
        Pedestrian rtrn = new Pedestrian(position);

        rtrn.health = this.health;
        rtrn.lastPosition = this.lastPosition;
        rtrn.destination = this.destination;
        rtrn.velocity = this.velocity;
        rtrn.recentlyDied = this.recentlyDied;

        return rtrn;
    }

    @Override
    public ArrayList<IInfluence> spread(IInfluence[][] blocked)
    {
        return me;
    }

    /**
     * Bewegt den Fußgänger auf sein bevorzugtes Feld.
     */
    public void move()
    {
        lastPosition = position;
        position = destination;
        destination = null;
    }

    /**
     * Fuegt dem Fussgaenger den entsprechenden Schaden zu. Fussgaenger stirbt
     * unter Umstaenden.
     *
     * @param damage Schaden, der zugefuegt werden soll
     */
    public void inflictDamage(int damage)
    {
        if (!recentlyDied)
        {
            health = health - damage > 0 ? health - damage : 0;
            if (health == 0)
            {
                recentlyDied = true;
                notifyOfDeath(new DiedEvent((this), this));
            }
        }
        else
        {
            System.out.println("Ich habe schlimmes verhindert");
        }
    }

    /**
     * Berechnet den bevorzugten Zug des Fußgängers
     *
     * @param dynamicField die dynamischen Einflüsse
     * @param staticField die statischen Einflüsse
     * @return den bevorzugten neuen Standort
     */
    public Point calcMove(FloorField dynamicField, FloorField staticField, IInfluence[][] blockedField, ArrayList<Point> emergencyExits)
    {
        // Kopie der Felder zum Arbeiten holen
        final byte[][] dyn = dynamicField.getField();
        final byte[][] stat = staticField.getField();
        final IInfluence[][] blocked = (IInfluence[][]) blockedField.clone();

        //Startwert der Beieinflussung der Umgebung
        int totalDynInfluenceArround = 0;
        int totalStatInfluenceArround = 0;

        //Präferenzenliste anlegen
        ArrayList<Point> pref = new ArrayList<Point>();

        //Liste mit Umgebung füllen

        pref.add(new Point(position.getX() - 1, position.getY() - 1));
        pref.add(new Point(position.getX() - 1, position.getY()));
        pref.add(new Point(position.getX() - 1, position.getY() + 1));
        pref.add(new Point(position.getX(), position.getY() - 1));
        pref.add(new Point(position.getX(), position.getY()));
        pref.add(new Point(position.getX(), position.getY() + 1));
        pref.add(new Point(position.getX() + 1, position.getY() - 1));
        pref.add(new Point(position.getX() + 1, position.getY()));
        pref.add(new Point(position.getX() + 1, position.getY() + 1));

        //Gesamt Beeinflussung in der Umgebung berechnen
        for (int i = 0; i < pref.size(); i++)
        {
            try
            {
                if (emergencyExits.contains(pref.get(i)))
                {
                    destination = pref.get(i);
                    return pref.get(i);
                }
                totalDynInfluenceArround += dyn[pref.get(i).getX()][pref.get(i).getY()];
                totalStatInfluenceArround += stat[pref.get(i).getX()][pref.get(i).getY()];
                if ((blocked[pref.get(i).getX()][pref.get(i).getY()] != null)
                        && (blocked[pref.get(i).getX()][pref.get(i).getY()] != this))
                {
                    pref.remove(i);
                    i--;
                }
            }
            catch (ArrayIndexOutOfBoundsException e)
            {
                pref.remove(i);
                i--;
                continue;
            }
        }

        final int sumInfluenceArround = totalDynInfluenceArround + totalStatInfluenceArround;

        if ((sumInfluenceArround == 0) || (pref.isEmpty()))
        {
            destination = position;
            velocity = 0;
        }
        else
        {

            //Liste nach Präferenz sortieren
            Collections.sort(pref, new Comparator<Point>()
            {
                @Override
                public int compare(Point a, Point b)
                {
                    if (blocked[a.getX()][a.getY()] instanceof EmergencyExit)
                    {
                        return 1;
                    }
                    else if (blocked[b.getX()][b.getY()] instanceof EmergencyExit)
                    {
                        return -1;
                    }
                    else
                    {
                        double valueA = ((dyn[a.getX()][a.getY()] + stat[a.getX()][a.getY()]) * 1.0 / Math.abs(sumInfluenceArround));
                        double valueB = ((dyn[b.getX()][b.getY()] + stat[b.getX()][b.getY()]) * 1.0 / Math.abs(sumInfluenceArround));
                        return (valueA > valueB) ? -1 : 1;
                    }
                }
            });

            //Ziel und Geschwindigkeit berechnen
            if (pref.size() > 1)
            {
                destination = pref.get(0).equals(lastPosition) ? pref.get(1) : pref.get(0);
            }
            else
            {
                destination = pref.get(0);
            }
            velocity = Math.abs((dyn[destination.getX()][destination.getY()] + stat[destination.getX()][destination.getY()]) / sumInfluenceArround - (dyn[position.getX()][position.getY()] + stat[position.getX()][position.getY()]) / sumInfluenceArround);
        }
        return destination;
    }

    @Override
    public int getBlocked(Point p)
    {
        if (position.equals(p))
        {
            return 1;
        }
        return 0;
    }

    @Override
    public ArrayList<Point> getBlockedFields()
    {
        ArrayList<Point> rtrn = new ArrayList<Point>();
        rtrn.add(position.clone());
        return rtrn;
    }

    @Override
    public boolean isBlocking()
    {
        return true;
    }

    @Override
    public boolean equals(Object o)
    {
        if (o == null)
        {
            return false;
        }
        if (!(o instanceof Pedestrian))
        {
            return false;
        }
        if (!(o.hashCode() == this.hashCode()))
        {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode()
    {
        int hash = 7;
        hash = 47 * hash + (this.position != null ? this.position.hashCode() : 0);
        hash = 47 * hash + (this.lastPosition != null ? this.lastPosition.hashCode() : 0);
        hash = 47 * hash + (this.destination != null ? this.destination.hashCode() : 0);
        hash = 47 * hash + (int) (Double.doubleToLongBits(this.velocity) ^ (Double.doubleToLongBits(this.velocity) >>> 32));
        hash = 47 * hash + this.health;
        return hash;
    }

    /**
     *
     * @param l Listener der hinzugefügt wird
     */
    public static void addListerner(DiedListener l)
    {
        if (toNotify == null)
        {
            toNotify = new EventListenerList();
        }
        toNotify.add(DiedListener.class, l);
    }

    public static void removeListerner(DiedListener l)
    {
        if (toNotify == null)
        {
            toNotify = new EventListenerList();
        }
        toNotify.remove(DiedListener.class, l);
    }

    protected synchronized void notifyOfDeath(DiedEvent event)
    {
        for (DiedListener l : toNotify.getListeners(DiedListener.class))
        {
            l.pedestrianDied(event);
        }
    }

    public synchronized void notifyOfRescue(ExitEvent event)
    {
        for (ExitListener l : toNotify.getListeners(ExitListener.class))
        {
            l.pedestianExit(event);
        }
    }
}
